/* 落子点元素构造函数 */
function DropPoint(x, y, i, j, dropPointElement = null) {
    this.x = x; //相对于父元素的 x 坐标
    this.y = y; //相对于父元素的 y 坐标

    this.i = i; //在棋盘数组中列位置索引
    this.j = j; //在棋盘数组中行位置索引

    this.belong = null //属于哪个玩家
    this.dropPointElement = dropPointElement //DOM元素（此处也可以采用存储元素ID）
}

/* 左半部分每一列需要隐藏的格子上半部分和下半部分个数 */
const HIDDEN_LEFT_CELL_LIMIT = [{ top: 7, bottom: 6 },
{ top: 6, bottom: 5 }, { top: 5, bottom: 4 },
{ top: 4, bottom: 3 }, { top: 3, bottom: 2 },
{ top: 2, bottom: 1 }, { top: 1, bottom: 0 }]

/* 落子点左半部分需要置为null的落子上半部分和下半部分个数 */
const DROP_LEFT_POINT_CONFIG_LIMIT = [{ top: 3, bottom: 6 },
{ top: 3, bottom: 5 }, { top: 2, bottom: 5 },
{ top: 2, bottom: 4 }, { top: 1, bottom: 4 },
{ top: 1, bottom: 3 }, { top: 0, bottom: 3 }]

/* 配置项 */
const CONFIG = {
    /* 每个格子的配置项 */
    CELL_CONFIG: {
        width: 41.8, //每个棋盘格子的宽度
        height: 57.3 //每个棋盘格子的高度
    },

    /* 奇偶列的落子点数量配置项 */
    DROP_POINT_CONFIG: {
        oddColumnLimit: 17, //奇数列棋盘格子个数(实际格子15个，填充两个方便找索引规律)
        evenColumnLimit: 17, //偶数列棋盘格子个数
    },

    /* 棋盘配置项 */
    CHESSBOARD_CONFIG: {
        row: 28, //棋盘行数
        column: 14 //棋盘列数
    },

    /* 棋子配置项 */
    PIECE_CONFIG: {
        width: 22.5, //棋子的宽度
        height: 22.5 //棋子的高度
    },

    /* 左右两部分每一列不存在的落子点的格子配置项 */
    NOT_EXIT_DROP_POINT_CONFIG: [...DROP_LEFT_POINT_CONFIG_LIMIT, { top: 0, bottom: 2 },
    ...DROP_LEFT_POINT_CONFIG_LIMIT.reverse()],

    /* 左右两部分每一列需要隐藏的格子配置项 */
    HIDDEN_CELL_CONFIG: [...HIDDEN_LEFT_CELL_LIMIT, ...HIDDEN_LEFT_CELL_LIMIT.reverse()]
}

/* 全局对象 */
const GLOBAL = {
    CELL_INDEX_ARR: [], //棋盘所有落点坐标（二维数组）

    CELL_EMPTY_ARR: [], //棋盘空落点坐标

    WIN_DROPS_ARR: [],//获胜数组

    /* 创建两个玩家 */
    playerArray: [{
        id: 1,
        pieceClass: 'piece_black', //棋子样式类名
    }, {
        id: 2,
        pieceClass: 'piece_white', //棋子样式类名
    }],

    currentPlayerIndex: 0,  //当前玩家索引

    isWin: false, //是否有人获胜

    isStart: false, //游戏是否开始

    isOpenAi: false, //是否开启人机 

    isAiDroped: true,//开启人机模式下人机是否已经落子

    withdrawStack: [], //悔棋栈(使用数组的push和pop实现栈功能)

    isOnline: false,//是否联机对战

    webSocket: null, //WebSocket 连接对象

    playerId: null, //联机状态下当前玩家的id

    playerIndex: null, //联机状态下玩家在玩家列表中的的索引

    gameId: null, //联机状态下当前对局id

    currentTimeStamp: new Date().getTime(),//当前时间，用于点击事件防抖

    // 客户端发送消息类型
    webSocketSendCode: {
        findOpponent: 3001, //查找对手
        sendDrop: 3002, //发送落子点
        giveUp: 3003, //投降跑路
        acceptMatch: 3004, //接受对局
        rejectMatch: 3005, //拒绝配对
        sendDropIndex: 3006 // 更新棋盘
    },

    //服务器响应消息状态码
    MESSAGE_STATE_CODE: {
        createPlayer: 200,  //创建玩家成功
        success: 201, //创建对局成功
        fail: 400, //创建对局失败
        findOpponent: 500, //成功找到对手玩家
        requestMatch: 10001,//请求对局
        updateDrop: 10002,//更新棋盘
        giveUp: 10003,//有人跑了
    }
}

/* 更新当前需要落子的玩家 */
function updatePlayerTip() {
    //找到提示元素
    let currentPlayerTipElement = document.getElementById('tip')
    let tipText = document.getElementById('tipText')

    //联机状态下更新玩家提示
    if (GLOBAL.isOnline) {
        tipText.innerText = (GLOBAL.currentPlayerIndex === GLOBAL.playerIndex) ? `己方` : `对手`
    }

    // 更新提示框的背景色
    tipText.style.color = (GLOBAL.currentPlayerIndex === 0) ? '#e0e0e0' : '#409eff'

    //默认提示第一个玩家(黑子先行)
    currentPlayerTipElement.classList = []
    currentPlayerTipElement.classList.add(GLOBAL.playerArray[GLOBAL.currentPlayerIndex].pieceClass)
}

/* 创建棋盘 */
function createChessboard() {
    //获取棋盘根元素
    const chessboard = document.getElementById('chessboard')

    //根据棋盘配置项从左往右构建
    for (let i = 0; i < CONFIG.CHESSBOARD_CONFIG.column; i++) {

        //创建一列格子容器
        let column = document.createElement('div');
        column.classList.add(`column`)

        //将左箭头格子和右箭头格子按照奇偶性质分别添加到格子容器
        for (let j = 0; j < CONFIG.CHESSBOARD_CONFIG.row; j++) {
            if (j % 2 === 0) {
                column.innerHTML += i % 2 === 0 ? `<div class="cell__left"></div>` : `<div class="cell__right"></div>`
            } else {
                column.innerHTML += i % 2 === 0 ? `<div class="cell__right"></div>` : `<div class="cell__left"></div>`
            }
        }

        //将这列添加到棋盘
        chessboard.appendChild(column)
    }

    //获取所有的列元素
    let columns = document.querySelectorAll('.column')

    //遍历每一列
    columns.forEach((col, i) => {
        //获取每一列的所有格子
        let cells = col.querySelectorAll('div')

        //遍历这一列的所有格子
        cells.forEach((cell, j) => {
            //根据每一列需要隐藏的格子配置项隐藏格子
            if ((j < CONFIG.HIDDEN_CELL_CONFIG[i].top) ||
                j >= (CONFIG.CHESSBOARD_CONFIG.row - CONFIG.HIDDEN_CELL_CONFIG[i].bottom)) {
                cell.style.visibility = `hidden`
            }
        })
    })
}


/* 生成落子点坐标数组 */
function createCellIndexArr() {
    //计算所有落子点坐标
    for (let i = 0; i < 15; i++) {
        let cellColumnIndexArr = []

        for (let j = 0; j < 34; j++) {
            if (i % 2 === 1) {
                if (j % 2 === 0) {
                    cellColumnIndexArr.push(new DropPoint
                        (i * CONFIG.CELL_CONFIG.width, j * (CONFIG.CELL_CONFIG.height / 2), i, j))
                }
            } else {
                if (j % 2 === 1) {
                    cellColumnIndexArr.push(new DropPoint
                        (i * CONFIG.CELL_CONFIG.width, j * (CONFIG.CELL_CONFIG.height / 2), i, j))
                }
            }
        }
        GLOBAL.CELL_INDEX_ARR.push(cellColumnIndexArr)
    }
}


/* 创建落子点 */
function createDropPoint() {
    //获取棋盘上层遮罩
    const chessboardTopMask = document.querySelector('#chessboard-top-mask')

    //根据落子点配置项将不在棋盘内的点置为null
    GLOBAL.CELL_INDEX_ARR.forEach((column, i) => {
        //获取当前列的落子点总数
        let pointLimit = (i % 2 === 0) ?
            CONFIG.DROP_POINT_CONFIG.oddColumnLimit : CONFIG.DROP_POINT_CONFIG.evenColumnLimit;

        for (let j = 0; j < pointLimit; j++) {
            if (j < CONFIG.NOT_EXIT_DROP_POINT_CONFIG[i].top || j >=
                (pointLimit - CONFIG.NOT_EXIT_DROP_POINT_CONFIG[i].bottom)) {
                column[j] = null
            }
        }
    })

    /* 创建落子点 */
    GLOBAL.CELL_INDEX_ARR.forEach((column, i) => {
        //获取当前列的落子点总数
        let pointLimit = (i % 2 === 0) ? CONFIG.DROP_POINT_CONFIG.oddColumnLimit : CONFIG.DROP_POINT_CONFIG.evenColumnLimit;

        //遍历每一列并将不为 null 的格子添加到上层遮罩上
        for (let j = 0; j < pointLimit; j++) {
            cell = column[j]

            if (cell) {
                let piece = createPiece(chessboardTopMask, cell.x, cell.y)

                //将列坐标和行坐标记录到html元素
                cell.i = i;
                cell.j = j;

                //落子点添加点击事件
                piece.onclick = onDrop.bind(piece, i, j);
                cell.dropPointElement = piece
            }

        }
    })
}

/* 落子点点击事件 */
function onDrop(i, j) {
    //点击事件防抖,间隔700ms和人机落子思考时间一致
    const currentTime = new Date().getTime()
    if (currentTime - GLOBAL.currentTimeStamp < 700) {
        return
    }

    //联机状态下，没轮到自己不许点击
    if (GLOBAL.isOnline && GLOBAL.playerIndex !== GLOBAL.currentPlayerIndex) {
        return
    }

    //开启人机状态下,人机没落子不允许落子
    if (GLOBAL.isOpenAi && !GLOBAL.isAiDroped) {
        return
    }

    //如果有人获胜则不允许落子
    if (GLOBAL.isWin) {
        return
    }

    //获取对手玩家索引
    let opponentIndex = (GLOBAL.currentPlayerIndex === 0) ? 1 : 0;

    //获得落子点
    let dropPiece = GLOBAL.CELL_INDEX_ARR[i][j];

    //如果落子点未被占领则落子
    if (dropPiece.belong === null) {
        //若开启联机则向服务器更新棋盘
        if (GLOBAL.isOnline) {
            GLOBAL.webSocket.send(JSON.stringify({
                type: GLOBAL.webSocketSendCode.sendDropIndex,
                data: {
                    playerIndex: GLOBAL.playerIndex,
                    i, j
                }
            }));

            return
        }

        //获取当前玩家
        const currentPlayer = GLOBAL.playerArray[GLOBAL.currentPlayerIndex];

        //根据玩家的棋子样式更改落子点样式
        this.classList.add(currentPlayer.pieceClass)

        //更改棋子的归属权
        dropPiece.belong = currentPlayer.id

        //将落子存入悔棋栈中
        GLOBAL.withdrawStack.push(dropPiece)

        //更新可落点数组
        updateEmptyCell();

        //判断是否获胜
        if (isVictory({ i, j })) {
            GLOBAL.WIN_DROPS_ARR.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'red')
            GLOBAL.isWin = true
            createWinMask()

            return
        }

        //更新当前玩家
        GLOBAL.currentPlayerIndex = opponentIndex

        //更新人机落子状态
        GLOBAL.isOpenAi && (GLOBAL.isAiDroped = false)

        //更新玩家提示
        updatePlayerTip()

        //播放落子声音
        playDropAudio()

        //更新防抖时间戳
        GLOBAL.currentTimeStamp = new Date().getTime()

        //人机还手
        if (GLOBAL.isOpenAi) {
            // 延迟三秒执行，模拟思考
            setTimeout(doAiDropV1, 700)
            // setTimeout(doAiDropV2, 700)
        }
    }
}

/* 创建棋子 */
function createPiece(el, x, y) {
    //创建棋子节点
    let piece = document.createElement('div')
    piece.classList.add('piece')

    //设置棋子偏移量
    piece.style.left = `${x - CONFIG.PIECE_CONFIG.width}px`
    piece.style.top = `${y - CONFIG.PIECE_CONFIG.height}px`

    // 将棋子添加到棋盘
    el.appendChild(piece)
    return piece
}

/* 判断是否胜利(参数为落子点的数组索引i为列,j为行) */
function isVictory(dropIndex) {
    //获取当前落子点
    const currentDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i][dropIndex.j]

    //获取所有垂直线、主副对角线的所有胜负点
    const { verticalLine, leadingDiagonal, counterDiagonal } = getWinDrops(dropIndex)

    //返回第一个连续相同的五个点
    let winDrops = null;

    const verticalLineWinDrops = findConsecutive(verticalLine);
    const leadingDiagonalWinDrops = findConsecutive(leadingDiagonal);
    const counterDiagonalWinDrops = findConsecutive(counterDiagonal);

    //垂直方向找
    if (Array.isArray(verticalLineWinDrops)) {
        winDrops = verticalLineWinDrops
    }

    //主对角线方向找
    if (Array.isArray(leadingDiagonalWinDrops)) {
        winDrops = leadingDiagonalWinDrops
    }

    //副对角线方向找
    if (Array.isArray(counterDiagonalWinDrops)) {
        winDrops = counterDiagonalWinDrops
    }

    //如果winDrops不为null则代表存在包含 dropIndex 在内的连续相同的5个点
    if (winDrops && winDrops.find((drop) => drop.x === currentDrop.x && drop.y === currentDrop.y)) {
        GLOBAL.WIN_DROPS_ARR = winDrops
        return true;
    }

    return false
}


/* 获取胜负点 (按垂直主副对角线三条线的形式返回，以下内容有存在可以逻辑复用的地方,但是为了容易阅读和理解没有合并,请勿略这个细节) */
function getWinDrops(dropIndex) {
    //存储以该落子点为中心的垂直线上的9个点
    let verticalLine = [];

    //存储以该落子点为中心的左上向右下直线上的9个点
    let leadingDiagonal = [];

    //存储以该落子点为中心的右上向左下直线上的9个点
    let counterDiagonal = [];


    //获取当前落点并添加索引属性 i 和 j 分别代表列和行
    const currentDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i][dropIndex.j];

    //存储当前落点
    verticalLine.push(currentDrop)
    leadingDiagonal.push(currentDrop)
    counterDiagonal.push(currentDrop)

    for (let j = 1; j < 5; j++) {
        let topDrop; //垂直上方的一个点
        let bottomDrop //垂直下方的一个点

        /* 垂直线上寻找9个点 */
        {
            //当前列第一个不为null的点的索引位置
            let currentColumTopStart = GLOBAL.CELL_INDEX_ARR[dropIndex.i].findIndex(item => item)

            //当前列最后一个不为null的点的索引位置
            let currentColumTopEnd = GLOBAL.CELL_INDEX_ARR[dropIndex.i].findIndex((item, index) => {
                return index > currentColumTopStart && !item
            }) - 1

            //垂直方向上边界
            if (dropIndex.j - j >= currentColumTopStart) {
                //当前列垂直向上找4个
                topDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i][dropIndex.j - j];

                if (topDrop) {
                    //存储索引信息
                    topDrop.i = dropIndex.i
                    topDrop.j = dropIndex.j - j
                    verticalLine.push(topDrop)
                }
            }

            //垂直方向下边界
            if (dropIndex.j - j < currentColumTopEnd) {
                //当前列垂直向下找4个
                bottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i][dropIndex.j + j];

                if (bottomDrop) {
                    //存储索引信息
                    bottomDrop.i = dropIndex.i
                    bottomDrop.j = dropIndex.j + j
                    verticalLine.push(bottomDrop)
                }
            }

        }

        let leftTopToRightBottomDrop; //坐上到右下的一个点
        let righBottomToLeftTopDrop; //右下到左上的一个点

        /* 主对角线上寻找9个点 */
        {
            //右边界
            if (dropIndex.i + j < CONFIG.CHESSBOARD_CONFIG.column + 1) {
                // 从左上往右下找4个
                if (dropIndex.i % 2 === 1) {
                    if (j < 2) {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j
                        }

                    } else if (j < 4) {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j + 1];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j + 1
                        }

                    } else {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j + 2];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j + 2
                        }
                    }
                } else {
                    if (j < 3) {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j + 1];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j + 1
                        }
                    } else {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j + 2];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j + 2
                        }
                    }
                }
            }


            //左边界
            if (dropIndex.i - j >= 0) {
                //从右下往左上数找4个
                if (dropIndex.i % 2 === 1) {
                    if (j < 3) {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j - 1];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j - 1
                        }
                    } else {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j - 2];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j - 2
                        }
                    }
                } else {
                    if (j < 2) {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j
                        }
                    } else if (j < 4) {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j - 1];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j - 1
                        }

                    } else {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j - 2];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j - 2
                        }
                    }
                }
            }

        }


        let rightTopToLeftBottomDrop; //右上到左下的一个点
        let leftBottomToRightTopDrop; //左下到右上的一个点

        /* 副对角线上寻找9个点 */
        {
            //右边界
            if (dropIndex.i + j < 15) {
                // 从右上往左下数找4个
                if (dropIndex.i % 2 === 1) {
                    if (j < 3) {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j - 1];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j - 1
                        }

                    } else {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j - 2];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j - 2
                        }
                    }
                } else {
                    if (j < 2) {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j
                        }
                    } else if (j < 4) {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j - 1];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j - 1
                        }

                    } else {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j - 2];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j - 2
                        }

                    }
                }
            }

            //左边界
            if (dropIndex.i - j >= 0) {
                //从左下往右上数找4个
                if (dropIndex.i % 2 === 1) {
                    if (j < 2) {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j
                        }

                    } else if (j < 4) {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j + 1];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j + 1
                        }
                    } else {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j + 2];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j + 2
                        }
                    }
                } else {
                    if (j < 3) {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j + 1];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j + 1
                        }
                    } else {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j + 2];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j + 2
                        }
                    }
                }
            }

        }


        //查找到的节点不为null则存储到对应的对角线数组上
        leftTopToRightBottomDrop && leadingDiagonal.push(leftTopToRightBottomDrop)
        righBottomToLeftTopDrop && leadingDiagonal.push(righBottomToLeftTopDrop)
        rightTopToLeftBottomDrop && counterDiagonal.push(rightTopToLeftBottomDrop)
        leftBottomToRightTopDrop && counterDiagonal.push(leftBottomToRightTopDrop)

    }

    /*   胜负点位置测试 */
    {
        // leadingDiagonal.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'red')
        // verticalLine.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'red')
        // counterDiagonal.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'red')

        // setInterval(() => {
        //     leadingDiagonal.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'transparent')
        //     verticalLine.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'transparent')
        //     counterDiagonal.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'transparent')
        // }, 2000)
    }


    //将胜负点按坐标位置排序后返回
    verticalLine.sort((a, b) => a.y - b.y)
    leadingDiagonal.sort((a, b) => a.x - b.x)
    counterDiagonal.sort((a, b) => a.x - b.x)
    return {
        verticalLine,
        leadingDiagonal,
        counterDiagonal,
    }
}

/* 获取胜负点v2 (按六个方向返回获胜点，以下内容有存在可以逻辑复用的地方,但是为了容易阅读和理解没有合并,请勿略这个细节) */
function getWinDropsV2(dropIndex) {
    //垂直上方的落点
    let topToBottomList = [];
    //垂直下方的落点
    let bottomToTopList = [];
    //主对角线上方的落点
    let leftTopToRightBottomList = [];
    //主对角线下方的落点
    let leftBottomToRightTopList = [];
    //副对角线上方的落点
    let rightTopToLeftBottomList = [];
    //副对角线下方的落点
    let rightBottomToLeftTopList = [];

    for (let j = 1; j < 5; j++) {
        let topDrop; //垂直上方的一个点
        let bottomDrop //垂直下方的一个点

        /* 垂直线上寻找8个点 */
        {
            //当前列第一个不为null的点的索引位置
            let currentColumTopStart = GLOBAL.CELL_INDEX_ARR[dropIndex.i].findIndex(item => item)

            //当前列最后一个不为null的点的索引位置
            let currentColumTopEnd = GLOBAL.CELL_INDEX_ARR[dropIndex.i].findIndex((item, index) => {
                return index > currentColumTopStart && !item
            }) - 1

            //垂直方向上边界
            if (dropIndex.j - j >= currentColumTopStart) {
                //当前列垂直向上找4个
                topDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i][dropIndex.j - j];

                if (topDrop) {
                    //存储索引信息
                    topDrop.i = dropIndex.i
                    topDrop.j = dropIndex.j - j
                    topToBottomList.push(topDrop)
                }
            }

            //垂直方向下边界
            if (dropIndex.j - j < currentColumTopEnd) {
                //当前列垂直向下找4个
                bottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i][dropIndex.j + j];

                if (bottomDrop) {
                    //存储索引信息
                    bottomDrop.i = dropIndex.i
                    bottomDrop.j = dropIndex.j + j
                    bottomToTopList.push(bottomDrop)
                }
            }

        }

        let leftTopToRightBottomDrop; //左上到右下的一个点
        let righBottomToLeftTopDrop; //右下到左上的一个点

        /* 主对角线上寻找8个点 */
        {
            //右边界
            if (dropIndex.i + j < CONFIG.CHESSBOARD_CONFIG.column + 1) {
                // 从左上往右下找4个
                if (dropIndex.i % 2 === 1) {
                    if (j < 2) {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j
                        }

                    } else if (j < 4) {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j + 1];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j + 1
                        }

                    } else {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j + 2];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j + 2
                        }
                    }
                } else {
                    if (j < 3) {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j + 1];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j + 1
                        }
                    } else {
                        leftTopToRightBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j + 2];

                        if (leftTopToRightBottomDrop) {
                            //存储索引信息
                            leftTopToRightBottomDrop.i = dropIndex.i + j
                            leftTopToRightBottomDrop.j = dropIndex.j + 2
                        }
                    }
                }
            }


            //左边界
            if (dropIndex.i - j >= 0) {
                //从右下往左上数找4个
                if (dropIndex.i % 2 === 1) {
                    if (j < 3) {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j - 1];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j - 1
                        }
                    } else {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j - 2];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j - 2
                        }
                    }
                } else {
                    if (j < 2) {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j
                        }
                    } else if (j < 4) {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j - 1];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j - 1
                        }

                    } else {
                        righBottomToLeftTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j - 2];

                        if (righBottomToLeftTopDrop) {
                            //存储索引信息
                            righBottomToLeftTopDrop.i = dropIndex.i - j
                            righBottomToLeftTopDrop.j = dropIndex.j - 2
                        }
                    }
                }
            }

        }


        let rightTopToLeftBottomDrop; //右上到左下的一个点
        let leftBottomToRightTopDrop; //左下到右上的一个点

        /* 副对角线上寻找8个点 */
        {
            //右边界
            if (dropIndex.i + j < 15) {
                // 从右上往左下数找4个
                if (dropIndex.i % 2 === 1) {
                    if (j < 3) {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j - 1];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j - 1
                        }

                    } else {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j - 2];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j - 2
                        }
                    }
                } else {
                    if (j < 2) {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j
                        }
                    } else if (j < 4) {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j - 1];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j - 1
                        }

                    } else {
                        rightTopToLeftBottomDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i + j][dropIndex.j - 2];

                        if (rightTopToLeftBottomDrop) {
                            //存储索引信息
                            rightTopToLeftBottomDrop.i = dropIndex.i + j
                            rightTopToLeftBottomDrop.j = dropIndex.j - 2
                        }

                    }
                }
            }

            //左边界
            if (dropIndex.i - j >= 0) {
                //从左下往右上数找4个
                if (dropIndex.i % 2 === 1) {
                    if (j < 2) {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j
                        }

                    } else if (j < 4) {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j + 1];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j + 1
                        }
                    } else {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j + 2];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j + 2
                        }
                    }
                } else {
                    if (j < 3) {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j + 1];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j + 1
                        }
                    } else {
                        leftBottomToRightTopDrop = GLOBAL.CELL_INDEX_ARR[dropIndex.i - j][dropIndex.j + 2];

                        if (leftBottomToRightTopDrop) {
                            //存储索引信息
                            leftBottomToRightTopDrop.i = dropIndex.i - j
                            leftBottomToRightTopDrop.j = dropIndex.j + 2
                        }
                    }
                }
            }

        }


        //查找到的节点不为null则存储到对应的对角线数组上
        leftTopToRightBottomDrop && leftTopToRightBottomList.push(leftTopToRightBottomDrop)
        righBottomToLeftTopDrop && rightBottomToLeftTopList.push(righBottomToLeftTopDrop)
        rightTopToLeftBottomDrop && rightTopToLeftBottomList.push(rightTopToLeftBottomDrop)
        leftBottomToRightTopDrop && leftBottomToRightTopList.push(leftBottomToRightTopDrop)

    }
    return [
        topToBottomList,
        bottomToTopList,
        leftTopToRightBottomList,
        rightBottomToLeftTopList,
        rightTopToLeftBottomList,
        leftBottomToRightTopList,
    ]
}

//测试五个点是否连续
//console.log(findConsecutive([1, 1, 1, 1, 1, 1, 5, 6]));

/* 从长度为 5-9 的数组中找到并返回连续相同的5个元素
若未找到则返回最多重复几个且返回最后一个重复元素的索引 */
function findConsecutive(array) {
    let maxCount = 1;      // 最多重复几个元素
    let lastIndex = 0;    // 记录最后一个连续重复元素的索引位置

    //初始化第一个具有归属权的落子在数组中的索引
    array.forEach((item, index) => {
        if (item.belong) {
            lastIndex = index
        }
    })

    for (let i = 0; i < array.length; i++) {
        let count = 1;
        let last = i;
        for (let j = i + 1; j < array.length; j++) {
            if (array[i].belong && array[i].belong === array[j].belong) {
                count++;
                last = j;
            } else {
                break;
            }
        }

        if (count === 5) {
            return array.slice(i, i + 5); //如果有连续5个重复元素则返回该数组
        } else if (count > maxCount) {
            maxCount = count;
            lastIndex = last; // 更新最后一个连续重复元素的索引位置
        }
    }
    return { maxCount, lastIndex };
}


/* 落子点测试 */
function testDropPoint(i, j) {
    const chessboardTopMask = document.querySelector('#chessboard-top-mask')
    //随机测试落地
    let testDropPoint = GLOBAL.CELL_INDEX_ARR[i][j];

    let piece = document.createElement('div')
    piece.classList.add('piece')

    //设置棋子偏移量
    piece.style.left = `${testDropPoint.x - CONFIG.PIECE_CONFIG.width}px`
    piece.style.top = `${testDropPoint.y - CONFIG.PIECE_CONFIG.height}px`

    piece.style.backgroundColor = `red`

    // 将棋子添加到棋盘
    chessboardTopMask.appendChild(piece)
}

/* 开始游戏或重新开始 */
function setGameState() {
    GLOBAL.isStart = !GLOBAL.isStart
    this.innerText = GLOBAL.isStart ? `重新开始` : `开始游戏`

    //联机状态下点击就是刷新
    if (GLOBAL.isOnline) {
        GLOBAL.webSocket.close()

        //创建输了的遮罩
        const failMask = document.createElement('div')
        failMask.style.backgroundColor = `#232628`
        failMask.style.color = `#409eff`
        failMask.classList.add('win-mask')
        failMask.innerText = `你输了😥`

        failMask.onclick = () => {
            failMask.remove()
            window.location.reload()
        }
        document.body.appendChild(failMask)
        return
    }

    if (GLOBAL.isStart) {
        //游戏开始不允许点击联机
        const onlineBtn = document.getElementById("online")
        onlineBtn.classList.add('menu-btn__disabled')

        createDropPoint()
    } else {
        //简单粗暴的重新开始 ----> 刷新页面
        window.location.reload()
    }
}

/* 悔棋 */
function withdrawDrop() {
    //若处于获胜状态或联机状态则失效
    if (GLOBAL.isWin || GLOBAL.isOnline) {
        return
    }

    //若开启人机则回退两步,否则回退一步
    if (GLOBAL.isOpenAi) {
        doWithdrawDrop(true)
        doWithdrawDrop(true)
    } else {
        doWithdrawDrop()
    }

    //更新可落点数组
    updateEmptyCell();
}

/* 执行悔棋 */
function doWithdrawDrop(isAi = false) {
    if (GLOBAL.withdrawStack.length > 1 || isAi) {
        //取出栈顶元素
        let dropPiece = GLOBAL.withdrawStack.pop()

        //清空落子点的归属权
        dropPiece.belong = null

        //清空落子点和玩家绑定的样式信息
        dropPiece.dropPointElement.classList = []

        //添加棋子默认样式
        dropPiece.dropPointElement.classList.add('piece')

        //更新当前玩家索引
        GLOBAL.currentPlayerIndex = (GLOBAL.currentPlayerIndex === 0) ? 1 : 0;

        //更新玩家提示
        updatePlayerTip()
    }
}

/* 设置人机状态 */
function setAIGamer() {
    //若处于获胜或联机状态则失效
    if (GLOBAL.isWin || GLOBAL.isOnline) {
        return
    }

    if (GLOBAL.isStart) {
        //轮到白子则人机落子
        if (GLOBAL.currentPlayerIndex === 1) {
            doAiDropV1()
            // doAiDropV2()
        }
        GLOBAL.isOpenAi = !GLOBAL.isOpenAi
        this.innerText = GLOBAL.isOpenAi ? `关闭人机` : `开启人机`
    }
}

/* 人机下棋v1 堵截策略*/
function doAiDropV1() {
    //当前玩家
    const currentPlayer = GLOBAL.playerArray[GLOBAL.currentPlayerIndex]

    //循环标志,从末尾往前找黑子
    let findBlackIndex = 1;
    //循环标志,从末尾往前找白子
    let findWhiteIndex = 2;


    let aiDropPiece = null;
    let currentDrop = null;

    //寻找人机的获胜点，没有则去堵黑子
    while (GLOBAL.withdrawStack.length - findWhiteIndex > 9 && !aiDropPiece) {
        //获取人机上一次落子
        let lastAiDrop = GLOBAL.withdrawStack[GLOBAL.withdrawStack.length - findWhiteIndex]

        //假设人机能赢的子
        let possibleLandingPoint = getAiDropV1(lastAiDrop)
        possibleLandingPoint && (possibleLandingPoint.belong = 2)

        //人机有能赢或能连成4个的落点则落子
        if (possibleLandingPoint && isVictory({ i: possibleLandingPoint.i, j: possibleLandingPoint.j })) {
            console.log("---");
            aiDropPiece = possibleLandingPoint
        } else {
            possibleLandingPoint && (possibleLandingPoint.belong = null)
        }

        findWhiteIndex += 2
    }



    //如果没有能赢的落点,则开始堵黑棋
    if (!aiDropPiece) {
        //前三次落子无论下哪里都能找到落点
        if (GLOBAL.withdrawStack.length < 4) {
            //获取最后一个落子
            currentDrop = GLOBAL.withdrawStack[GLOBAL.withdrawStack.length - 1]

            //人机落子
            aiDropPiece = getAiDropV1(currentDrop);
        } else {
            //从第四次开始从悔棋栈栈顶开始查找,直到找到能堵黑棋的点
            while (GLOBAL.withdrawStack.length - findBlackIndex > 3 && !aiDropPiece) {
                //获取上一个黑子落子
                currentDrop = GLOBAL.withdrawStack[GLOBAL.withdrawStack.length - findBlackIndex]
                //人机落子
                aiDropPiece = getAiDropV1(currentDrop);
                findBlackIndex += 2;
            }
        }
    }

    //如果找不到能堵黑棋的点又没有能赢的点(无处可走就随机走,摆烂了🤮🤮🤮)
    if (!aiDropPiece) {
        aiDropIndex = Math.floor(Math.random() * GLOBAL.CELL_EMPTY_ARR.length)

        aiDropPiece = GLOBAL.CELL_EMPTY_ARR[aiDropIndex]
        console.log("人机随便下的😒");
    }


    //如果没找到落点则采用获胜策略
    // if (!aiDropPiece) {
    //     doAiDropV2()
    //     return
    // }


    //更改落子点的样式
    aiDropPiece.dropPointElement.classList.add(currentPlayer.pieceClass)

    //更改棋子的归属权
    aiDropPiece.belong = currentPlayer.id

    //将落子存入悔棋栈中
    GLOBAL.withdrawStack.push(aiDropPiece)

    //更新可落点数组
    updateEmptyCell();

    if (isVictory({ i: aiDropPiece.i, j: aiDropPiece.j })) {
        GLOBAL.WIN_DROPS_ARR.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'red')
        GLOBAL.isWin = true
        createWinMask()

        return
    }

    //更新当前玩家
    GLOBAL.currentPlayerIndex = (currentPlayer.id === 0) ? 1 : 0;
    //更新玩家提示
    updatePlayerTip()

    //更新人机落子状态
    GLOBAL.isAiDroped = true

    //播放落子声音
    playDropAudio()
}

/* 人机下棋v2 获胜策略*/
function doAiDropV2() {
    //当前玩家
    const currentPlayer = GLOBAL.playerArray[GLOBAL.currentPlayerIndex]

    let aiDropPiece = getAiDropV2()

    //更改落子点的样式
    aiDropPiece.dropPointElement.classList.add(currentPlayer.pieceClass)

    //更改棋子的归属权
    aiDropPiece.belong = currentPlayer.id

    //将落子存入悔棋栈中
    GLOBAL.withdrawStack.push(aiDropPiece)

    //更新可落点数组
    updateEmptyCell();

    if (isVictory({ i: aiDropPiece.i, j: aiDropPiece.j })) {
        GLOBAL.WIN_DROPS_ARR.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'red')
        GLOBAL.isWin = true
        createWinMask()

        return
    }

    //更新当前玩家
    GLOBAL.currentPlayerIndex = (currentPlayer.id === 0) ? 1 : 0;
    //更新玩家提示
    updatePlayerTip()

    //更新人机落子状态
    GLOBAL.isAiDroped = true

    //播放落子声音
    playDropAudio()
}

/* 播放落子声音 */
function playDropAudio() {
    const audio = document.querySelector('#stoneSound')
    audio.play()
}

/* 返回人机落子点v1 */
function getAiDropV1(currentDrop) {
    //获取该落子的所有获胜点
    const { verticalLine, leadingDiagonal, counterDiagonal } = getWinDrops({ i: currentDrop.i, j: currentDrop.j })

    //存储垂直线、主副对角线的所有元素
    const lineArray = [verticalLine, leadingDiagonal, counterDiagonal]

    //随机标志从前开始查找还是从后开始查找
    const randomSearchTag = Math.floor(Math.random() * 2);

    //人机落子在该线上的索引
    let aiDropIndex = null;

    //获得垂直线、主副对角线上分别最多连续重复多少个元素
    const verticalLineConsecutive = findConsecutive(verticalLine);
    const leadingDiagonalConsecutive = findConsecutive(leadingDiagonal);
    const counterDiagonalConsecutive = findConsecutive(counterDiagonal);

    //为数组对象添加自定义属性标识最多连续重复多少个元素以及最后一个重复元素的索引位置
    verticalLine.maxConsecutive = verticalLineConsecutive
    leadingDiagonal.maxConsecutive = leadingDiagonalConsecutive
    counterDiagonal.maxConsecutive = counterDiagonalConsecutive

    //找到最多连续重复几个
    const maxConsecutiveCount = Math.max(verticalLineConsecutive.maxCount, leadingDiagonalConsecutive.maxCount, counterDiagonalConsecutive.maxCount)

    //如果连续重复3个点，则获取所有连续重复三个点的线直接进行围堵
    if (maxConsecutiveCount === 3) {
        const closeToWinningLineArray = lineArray.filter((line) => line.maxConsecutive.maxCount === 3)
        for (let i = 0; i < closeToWinningLineArray.length; i++) {

            let opponentWillWinDrop = null;
            const closeToWinningLine = closeToWinningLineArray[i]
            let closeToWinningIndex = null

            if (randomSearchTag === 0) {
                if (closeToWinningLine.maxConsecutive.lastIndex + 1 < closeToWinningLine.length && !closeToWinningLine[closeToWinningLine.maxConsecutive.lastIndex + 1].belong) {
                    closeToWinningIndex = closeToWinningLine.maxConsecutive.lastIndex + 1
                    console.log("11");

                    //假定该棋子归属黑子
                    opponentWillWinDrop = closeToWinningLine[closeToWinningIndex]
                    opponentWillWinDrop.belong = 1

                    //判断是否能赢，能就下这里堵他！🤣
                    if (isVictory(opponentWillWinDrop)) {
                        console.log("t");

                        //清空归属权
                        opponentWillWinDrop.belong = null
                        //直接返回该落点
                        return opponentWillWinDrop
                    }
                    opponentWillWinDrop.belong = null
                }
                else
                    if (closeToWinningLine.maxConsecutive.lastIndex - closeToWinningLine.maxConsecutive.maxCount >= 0 && !closeToWinningLine[closeToWinningLine.maxConsecutive.lastIndex - closeToWinningLine.maxConsecutive.maxCount].belong) {
                        closeToWinningIndex = closeToWinningLine.maxConsecutive.lastIndex - closeToWinningLine.maxConsecutive.maxCount
                        console.log("22");

                        //假定该棋子归属黑子
                        opponentWillWinDrop = closeToWinningLine[closeToWinningIndex]
                        opponentWillWinDrop.belong = 1
                        //判断是否能赢，能就下这里堵他！🤣
                        if (isVictory(opponentWillWinDrop)) {
                            console.log("k");

                            //清空归属权
                            opponentWillWinDrop.belong = null
                            //直接返回该落点
                            return opponentWillWinDrop
                        }

                        opponentWillWinDrop.belong = null
                    }
            } else {
                if (closeToWinningLine.maxConsecutive.lastIndex - closeToWinningLine.maxConsecutive.maxCount >= 0 && !closeToWinningLine[closeToWinningLine.maxConsecutive.lastIndex - closeToWinningLine.maxConsecutive.maxCount].belong) {
                    closeToWinningIndex = closeToWinningLine.maxConsecutive.lastIndex - closeToWinningLine.maxConsecutive.maxCount
                    console.log("33");

                    //假定该棋子归属黑子
                    opponentWillWinDrop = closeToWinningLine[closeToWinningIndex]
                    opponentWillWinDrop.belong = 1
                    //判断是否能赢，能就下这里堵他！🤣
                    if (isVictory(opponentWillWinDrop)) {
                        console.log("j");

                        //清空归属权
                        opponentWillWinDrop.belong = null
                        //直接返回该落点
                        return opponentWillWinDrop
                    }

                    opponentWillWinDrop.belong = null
                }
                else
                    if (closeToWinningLine.maxConsecutive.lastIndex + 1 < closeToWinningLine.length && !closeToWinningLine[closeToWinningLine.maxConsecutive.lastIndex + 1].belong) {
                        closeToWinningIndex = closeToWinningLine.maxConsecutive.lastIndex + 1
                        console.log("44");

                        //假定该棋子归属黑子
                        opponentWillWinDrop = closeToWinningLine[closeToWinningIndex]
                        opponentWillWinDrop.belong = 1
                        //判断是否能赢，能就下这里堵他！🤣
                        if (isVictory(opponentWillWinDrop)) {
                            console.log("55");
                            //清空归属权
                            opponentWillWinDrop.belong = null
                            //直接返回该落点
                            return opponentWillWinDrop
                        }

                        opponentWillWinDrop.belong = null
                    }
            }
        }
    }


    //存储该线上空的点
    let emptyDrops = null;
    let maxConsecutiveLine = null;

    //重复查找直到找到一条有可落点的线
    while (!emptyDrops) {
        //最多连续重复点的线
        maxConsecutiveLine = getRandomLine(maxConsecutiveCount, lineArray)

        //获取该直线上所有空的点
        emptyDrops = maxConsecutiveLine.filter((drop) => {
            return !drop.belong
        })

    }

    //获取最后一个落子在该线上的索引
    let currentDropIndex = maxConsecutiveLine.findIndex((drop) => drop === currentDrop)


    if (!aiDropIndex) {
        //如果存在最长连续相同点则在最前或最尾处落子
        if (maxConsecutiveLine.maxConsecutive.maxCount > 1) {
            let opponentWillWinDrop = null;

            if (randomSearchTag === 0) {
                if (maxConsecutiveLine.maxConsecutive.lastIndex + 1 < maxConsecutiveLine.length && !maxConsecutiveLine[maxConsecutiveLine.maxConsecutive.lastIndex + 1].belong) {
                    aiDropIndex = maxConsecutiveLine.maxConsecutive.lastIndex + 1
                    console.log("1");

                    //假定该棋子归属黑子
                    opponentWillWinDrop = maxConsecutiveLine[aiDropIndex]
                    opponentWillWinDrop.belong = 1
                    //判断是否能赢，能就下这里堵他！🤣
                    if (isVictory(opponentWillWinDrop)) {
                        console.log("a");

                        //清空归属权
                        opponentWillWinDrop.belong = null
                        //直接返回该落点
                        return opponentWillWinDrop
                    }
                    opponentWillWinDrop.belong = null
                }
                else
                    if (maxConsecutiveLine.maxConsecutive.lastIndex - maxConsecutiveLine.maxConsecutive.maxCount >= 0 && !maxConsecutiveLine[maxConsecutiveLine.maxConsecutive.lastIndex - maxConsecutiveLine.maxConsecutive.maxCount].belong) {
                        aiDropIndex = maxConsecutiveLine.maxConsecutive.lastIndex - maxConsecutiveLine.maxConsecutive.maxCount
                        console.log("2");

                        //假定该棋子归属黑子
                        opponentWillWinDrop = maxConsecutiveLine[aiDropIndex]
                        opponentWillWinDrop.belong = 1
                        //判断是否能赢，能就下这里堵他！🤣
                        if (isVictory(opponentWillWinDrop)) {
                            console.log("b");

                            //清空归属权
                            opponentWillWinDrop.belong = null
                            //直接返回该落点
                            return opponentWillWinDrop
                        }

                        opponentWillWinDrop.belong = null
                    }
            } else {
                if (maxConsecutiveLine.maxConsecutive.lastIndex - maxConsecutiveLine.maxConsecutive.maxCount >= 0 && !maxConsecutiveLine[maxConsecutiveLine.maxConsecutive.lastIndex - maxConsecutiveLine.maxConsecutive.maxCount].belong) {
                    aiDropIndex = maxConsecutiveLine.maxConsecutive.lastIndex - maxConsecutiveLine.maxConsecutive.maxCount
                    console.log("3");

                    //假定该棋子归属黑子
                    opponentWillWinDrop = maxConsecutiveLine[aiDropIndex]
                    opponentWillWinDrop.belong = 1
                    //判断是否能赢，能就下这里堵他！🤣
                    if (isVictory(opponentWillWinDrop)) {
                        console.log("c");

                        //清空归属权
                        opponentWillWinDrop.belong = null
                        //直接返回该落点
                        return opponentWillWinDrop
                    }

                    opponentWillWinDrop.belong = null
                }
                else
                    if (maxConsecutiveLine.maxConsecutive.lastIndex + 1 < maxConsecutiveLine.length && !maxConsecutiveLine[maxConsecutiveLine.maxConsecutive.lastIndex + 1].belong) {
                        aiDropIndex = maxConsecutiveLine.maxConsecutive.lastIndex + 1
                        console.log("4");

                        //假定该棋子归属黑子
                        opponentWillWinDrop = maxConsecutiveLine[aiDropIndex]
                        opponentWillWinDrop.belong = 1
                        //判断是否能赢，能就下这里堵他！🤣
                        if (isVictory(opponentWillWinDrop)) {
                            console.log("d");

                            //清空归属权
                            opponentWillWinDrop.belong = null
                            //直接返回该落点
                            return opponentWillWinDrop
                        }

                        opponentWillWinDrop.belong = null
                    }
            }
        }
    }

    //不存在最长连续相同点的线
    if (!aiDropIndex) {
        //查找最靠近当前落点的一个可落点索引
        for (let start = currentDropIndex, i = 1, j = 1; start < maxConsecutiveLine.length; start++) {
            if (randomSearchTag === 0) {
                //向前查找
                if (start - i >= 0) {
                    //如果已经堵住了就不找了
                    if (maxConsecutiveLine[start - i].belong && maxConsecutiveLine[start - i].belong === currentDrop.belong) {
                        break
                    }

                    if (!maxConsecutiveLine[start - i].belong) {
                        aiDropIndex = start - i;
                        console.log("5");
                        break;
                    }
                    i++;
                }

                //向后查找
                if (!aiDropIndex && start + j < maxConsecutiveLine.length) {
                    //如果已经堵住了就不找了
                    if (maxConsecutiveLine[start + j].belong && maxConsecutiveLine[start + j].belong === currentDrop.belong) {
                        break
                    }
                    if (!maxConsecutiveLine[start + j].belong) {
                        aiDropIndex = start + j;
                        console.log("6");
                        break;
                    }
                    j++;
                }
            } else {
                //向后查找
                if (start + j < maxConsecutiveLine.length) {
                    //如果已经堵住了就不找了
                    if (maxConsecutiveLine[start + j].belong && maxConsecutiveLine[start + j].belong === currentDrop.belong) {
                        break
                    }

                    if (!maxConsecutiveLine[start + j].belong) {
                        aiDropIndex = start + j;
                        console.log("7");
                        break;
                    }
                    j++;
                }

                //向前查找
                if (!aiDropIndex && start - i >= 0) {
                    //如果已经堵住了就不找了
                    if (maxConsecutiveLine[start - i].belong && maxConsecutiveLine[start - i].belong === currentDrop.belong) {
                        break
                    }

                    if (!maxConsecutiveLine[start - i].belong) {
                        aiDropIndex = start - i;
                        console.log("8");
                        break;
                    }
                    i++;
                }
            }

        }
    }

    //返回人机落子
    return maxConsecutiveLine[aiDropIndex];
}

/* 返回人机落子点v2 */
function getAiDropV2() {
    //最终落子点
    let dropPiece = {
        score: -Infinity, //落子点得分
        i: null, //落子点列坐标
        j: null //落子点行坐标
    }

    //遍历悔棋栈（即所有已落点）
    GLOBAL.withdrawStack.reverse().forEach((withdrawStackdrop, index, arr) => {
        let verticalLineDrop = {
            score: -Infinity, //落子点得分
            i: null, //落子点列坐标
            j: null //落子点行坐标
        }
        let leadingDiagonalDrop = {
            score: -Infinity, //落子点得分
            i: null, //落子点列坐标
            j: null //落子点行坐标
        }
        let counterDiagonalDrop = {
            score: -Infinity, //落子点得分
            i: null, //落子点列坐标
            j: null //落子点行坐标
        }

        //获取该落子的所有获胜点
        const dropList = getWinDropsV2({ i: withdrawStackdrop.i, j: withdrawStackdrop.j })

        //过滤掉不够四个落子和已经被黑子堵住的方向
        const canDropList = dropList.filter(list => list.length === 4 && !list.includes((item) => { item.belong === 1 }))

        //获取活三的方向
        const live3 = canDropList.filter(list => list.filter((dropPoint) => dropPoint.belong === 2).length === 3)
        if (live3) {
            
        }

    })

    return GLOBAL.CELL_INDEX_ARR[5][5]
}

/* 获取落子点得分 */
function getDropScore(i, j) {
    let score = 0
    //假想落子点
    const assumptionDropPiece = GLOBAL.CELL_INDEX_ARR[i][j]
    //假设白子落子
    assumptionDropPiece.belong = 1

    //获取该落子的所有获胜点
    let { verticalLine, leadingDiagonal, counterDiagonal } = getWinDrops({ i: i, j: j })
    let verticalLineMaxCount = findConsecutive(verticalLine)
    let leadingDiagonalMaxCount = findConsecutive(leadingDiagonal)
    let counterDiagonalMaxCount = findConsecutive(counterDiagonal)

    //若有存在落点可以实现5个点连续，则直接返回最大值
    if (verticalLineMaxCount instanceof Array || leadingDiagonalMaxCount instanceof Array || counterDiagonalMaxCount instanceof Array) {
        //恢复棋子归属权
        assumptionDropPiece.belong = null
        return Infinity
    }

    //标记最长连续点所在线的归属权
    verticalLineMaxCount.belong = verticalLine[verticalLineMaxCount.lastIndex].belong
    leadingDiagonalMaxCount.belong = leadingDiagonal[leadingDiagonalMaxCount.lastIndex].belong
    counterDiagonalMaxCount.belong = counterDiagonal[counterDiagonalMaxCount.lastIndex].belong

    //计算三个方向上的得分
    new Array(verticalLineMaxCount, leadingDiagonalMaxCount, counterDiagonalMaxCount).forEach((line) => {
        if (line.belong === 2) {
            if (line.maxCount === 4) {
                score += 1000
            } else if (line.maxCount === 3) {
                score += 100
            } else if (line.maxCount === 2) {
                score += 10
            } else if (line.maxCount === 1) {
                score += 1
            }
        } else if (line.belong === 1) {
            if (line.maxCount === 4) {
                score -= 999
            } else if (line.maxCount === 3) {
                score -= 99
            } else if (line.maxCount === 2) {
                score -= 9
            } else if (line.maxCount === 1) {
                score -= 1
            }
        }
    })

    //返回总分
    return score
}


/* 更新空落点数组 */
function updateEmptyCell() {
    GLOBAL.CELL_INDEX_ARR.forEach((column, i) => {
        column.forEach((drop, j) => {
            if (drop) {
                drop.i = i
                drop.j = j
                GLOBAL.CELL_EMPTY_ARR.push(drop)
            }
        })
    })
}

/* 随机返回一条可以落子的线 */
function getRandomLine(maxConsecutiveCount, lineArray) {
    //若最大连续重复的点为1,则随机从三条线挑一条进行第一次落子
    let randomLineIndex = null;
    if (maxConsecutiveCount === 1 && lineArray.length > 1) {
        randomLineIndex = Math.floor(Math.random() * lineArray.length);
    } else if (maxConsecutiveCount === 1) {
        randomLineIndex = 0
    }

    //找到最多连续的那条线
    const maxConsecutiveLine = lineArray.find((line, index) => {
        //随机挑一条线落子
        if (randomLineIndex) {
            return index === randomLineIndex
        }
        return line.maxConsecutive.maxCount === maxConsecutiveCount
    })

    //查找过的线则移除不再查找
    randomLineIndex && randomLineIndex !== 0 && delete lineArray[randomLineIndex]
    return maxConsecutiveLine;
}


/* 开启联机模式 */
function startOnlineGaming() {
    //游戏中途不允许联机
    if (GLOBAL.isStart) {
        return
    }

    const opponentID = window.prompt('输入对战玩家的ID');

    if (+opponentID === GLOBAL.playerId) {
        alert("无效ID")
    } else if (opponentID) {
        const opponent = {
            type: GLOBAL.webSocketSendCode.findOpponent,
            data: {
                opponentID: +opponentID,
            }
        }
        GLOBAL.webSocket.send(JSON.stringify(opponent))
    }

}

/* 设置按钮状态 */
function setBtnState() {
    //联机时不允许悔棋和开启人机
    const aiBtn = document.getElementById('ai')
    const withdrawBtn = document.getElementById('withdraw')
    const startBtn = document.getElementById('start')
    const onlineBtn = document.getElementById('online')

    if (GLOBAL.isOnline && GLOBAL.isStart) {
        startBtn.innerText = `掀翻桌子`

        //联机按钮不允许点击
        onlineBtn.classList.add('menu-btn__disabled')
        withdrawBtn.classList.add('menu-btn__disabled')
        aiBtn.classList.add('menu-btn__disabled')
    } else {
        // 恢复按钮初始状态
        startBtn.innerText = `开始游戏`

        onlineBtn.classList = []
        withdrawBtn.classList = []
        aiBtn.classList = []

        //联机按钮不允许点击
        onlineBtn.classList.add('menu-btn')
        withdrawBtn.classList.add('menu-btn')
        aiBtn.classList.add('menu-btn')
    }
}

/* 建立webSocket连接 */
function initWS() {
    //显示玩家id
    const playerId = document.getElementById('online-ID')
    playerId.style.visibility = 'visible'
    // GLOBAL.webSocket = new WebSocket('ws://127.0.0.1:3000')
    GLOBAL.webSocket = new WebSocket('ws://42.192.167.72:3000')

    GLOBAL.webSocket.onmessage = (message) => {
        const messageObj = JSON.parse(message.data)
        console.log(messageObj);
        if (messageObj.type === GLOBAL.MESSAGE_STATE_CODE.createPlayer) {
            //存储当前玩家id
            GLOBAL.playerId = messageObj.data
            playerId.childNodes[1].innerText = `${GLOBAL.playerId}`

        } else if (messageObj.type === GLOBAL.MESSAGE_STATE_CODE.findOpponent) {
            //创建等待遮罩
            const loadingMask = document.createElement('div')
            loadingMask.id = "loadingMask"
            loadingMask.style.backgroundColor = `#232628`
            loadingMask.style.color = `#409eff`
            loadingMask.classList.add('win-mask')
            loadingMask.innerText = `等待对手接受☕`

            document.body.appendChild(loadingMask)

        } else if (messageObj.type === GLOBAL.MESSAGE_STATE_CODE.requestMatch) {
            const result = window.confirm(`ID:${messageObj.data} 选手请求与你对战😎`)
            console.log({ result });
            if (result) {
                //接受对战
                GLOBAL.webSocket.send(JSON.stringify({
                    type: GLOBAL.webSocketSendCode.acceptMatch,
                    data: {
                        opponentID: messageObj.data,
                    }
                }))

            } else {
                // 拒绝对战
                GLOBAL.webSocket.send(JSON.stringify({
                    type: GLOBAL.webSocketSendCode.rejectMatch,
                    data: {
                        opponentID: messageObj.data,
                    }
                }))
            }


        } else if (messageObj.type === GLOBAL.MESSAGE_STATE_CODE.fail) {
            //提醒被拒绝了
            window.alert("创建棋局失败")

            //被拒绝了就直接刷新页面,当作什么都没发生过🙌
            window.location.reload()

        } else if (messageObj.type === GLOBAL.MESSAGE_STATE_CODE.success) {
            GLOBAL.playerIndex = messageObj.data.playerIndex

            GLOBAL.gameId = messageObj.data.gameId


            //移除等待遮罩
            const loadingMask = document.getElementById('loadingMask')
            loadingMask?.remove()

            //修改全局状态为联机模式
            GLOBAL.isOnline = true

            //更新玩家提示
            updatePlayerTip()

            //开始游戏
            GLOBAL.isStart = true
            setBtnState()
            createDropPoint()
        } else if (messageObj.type === GLOBAL.MESSAGE_STATE_CODE.updateDrop) {
            console.log(messageObj);
            //更新棋盘
            onlineUpdateDrop(messageObj.data.i, messageObj.data.j, messageObj.data.playerIndex)
        } else if (messageObj.type === GLOBAL.MESSAGE_STATE_CODE.giveUp) {
            GLOBAL.isOnline = false
            GLOBAL.isStart = false

            //如果还没赢就断开连接那就是有人跑了，嘲讽他🤮
            if (!GLOBAL.isWin) {
                const startBtn = document.getElementById('start')
                startBtn.innerText = `重新开始`
                startBtn.onclick = () => {
                    window.location.reload()
                }

                //创建别人跑了获胜的遮罩
                const runMask = document.createElement('div')
                runMask.id = "runMask"
                runMask.style.backgroundColor = `#232628`
                runMask.style.color = `#409eff`
                runMask.classList.add('win-mask')
                runMask.innerText = `有人掀桌子了😒`
                runMask.onclick = () => {
                    runMask.remove()

                }

                document.body.appendChild(runMask)
            }

        }
    };

    //断开连接时发送游戏id，服务器销毁该对局
    GLOBAL.webSocket.onclose = () => {
        GLOBAL.webSocket = null
    }
}

/* 联机状态下更新棋盘 */
function onlineUpdateDrop(i, j, playerIndex) {
    const dropPiece = GLOBAL.CELL_INDEX_ARR[i][j]

    //获取玩家索引对应的玩家
    const player = GLOBAL.playerArray[playerIndex];

    //根据玩家的棋子样式更改落子点样式
    dropPiece.dropPointElement.classList.add(player.pieceClass)

    //更改棋子的归属权
    dropPiece.belong = player.id

    //判断是否获胜
    if (isVictory({ i, j })) {
        GLOBAL.isOnline = false
        GLOBAL.isStart = false

        const startBtn = document.getElementById('start')
        startBtn.innerText = `重新开始`

        startBtn.onclick = () => window.location.reload()

        GLOBAL.WIN_DROPS_ARR.forEach((drop) => drop.dropPointElement.style.backgroundColor = 'red')
        GLOBAL.isWin = true
        createWinMask()
        return
    }

    //更新当前玩家
    GLOBAL.currentPlayerIndex = playerIndex === 0 ? 1 : 0

    //更新玩家提示
    updatePlayerTip()

    //播放落子声音
    playDropAudio()
}

/* 创建获胜遮罩 */
function createWinMask() {
    const winMask = document.createElement('div')

    let text = `${GLOBAL.currentPlayerIndex === 0 ? `🎉黑子` : '🎉白子'}获胜🎊`
    let textColor = `${GLOBAL.currentPlayerIndex === 0 ? `#000` : '#fff'}`
    let backgroundColor = `${GLOBAL.currentPlayerIndex === 0 ? `#fff` : '#000'}`

    //人机赢啦,嘲讽一下
    if (GLOBAL.isOpenAi && GLOBAL.currentPlayerIndex === 1) {
        text = `你输给人机了!😓`
        textColor = `red`
        backgroundColor = `blue`
    }

    winMask.innerText = text
    winMask.classList.add('win-mask')
    winMask.style.color = textColor
    winMask.style.backgroundColor = backgroundColor

    //播放获胜音效
    document.querySelector('#cheer').play()

    winMask.addEventListener('click', () => {
        winMask.remove()
    })
    document.body.appendChild(winMask)
}

/* 随机测试 */
function test() {
    console.log(GLOBAL.CELL_INDEX_ARR[0][16]);
}

/* 点击复制用户id */
function copyPlayerId() {
    if (GLOBAL.isStart) {
        return
    }

    const selection = window.getSelection();
    const range = document.createRange();
    range.selectNodeContents(this.childNodes[1]);
    selection.removeAllRanges();
    selection.addRange(range);
    document.execCommand('copy');
    window.alert('ID已复制')
    selection.removeAllRanges();
}


/* 文档树构建完成后开始创建游戏 */
document.addEventListener('DOMContentLoaded', () => {
    createChessboard()
    createCellIndexArr();
    initWS();

    //获取菜单三个按钮
    const startBtn = document.getElementById('start')
    const withdrawBtn = document.getElementById('withdraw')
    const aiBtn = document.getElementById('ai')
    const onlineBtn = document.getElementById('online')
    const playIdTip = document.getElementById('online-ID')

    //为菜单各个按钮添加点击事件
    startBtn.onclick = setGameState
    withdrawBtn.onclick = withdrawDrop
    aiBtn.onclick = setAIGamer
    onlineBtn.onclick = startOnlineGaming
    playIdTip.onclick = copyPlayerId

    //测试棋盘落点
    // testDropPoint(7, 10)
    updatePlayerTip()

    //test()
})

